from google.appengine.api import users
from google.appengine.ext import db
from google.appengine.ext.db import polymodel
import logging

class Project(db.Model):
    name = db.StringProperty(required=True)
    description = db.StringProperty(multiline=True)
    opened_by = db.UserProperty()
    def get_iterations(self):
        query = Iteration.all()
        query.filter('project =', self)
        return query
    def get_active_iterations(self):
        query = Iteration.all()
        query.filter('project =', self)
        query.filter('status =', 'Active')
        return query.run()
    def get_backlog(self):
        query = ProjectBacklog.all()
        query.filter('project =', self)
        return query.get()
    def get_members(self):
        query = ProjectMember().all()
        query.filter('project = ', self)
        query.filter('status = ', 'collaborator')
        result = query.run()
        array = [m.member for m in result]
        return sorted(array, key=lambda item: item.user)
    def get_members_not_assigned(self):
        query = ProjectMember().all()
        query.filter('project = ', self)
        query.filter('status = ', 'not collaborator')
        result = query.run()
        members = [item.member for item in result]
        return members


    def is_user_valid(self):
        if users.is_current_user_admin():
            return True
        query = ProjectMember().all()
        member = Member.get_current_member()
        query.filter('member = ', member)
        query.filter('project = ', self)
        pm = query.get()
        return pm and pm.status == 'collaborator'

    def user_status(self):
        member = Member.get_current_member()
        query = ProjectMember.all()
        query.filter('project = ', self)
        query.filter('member = ', member)
        pmember = query.get()
        if pmember:
            return pmember.status
        else:
            return "no_request"

    @classmethod
    def get_projects(cls):
        projects = None
        if users.is_current_user_admin():
            projects_query = cls.all()
            projects = projects_query.run()
        else:
            query = ProjectMember().all()
            member = Member.get_current_member()
            query.filter('member = ', member)
            query.filter('status = ', 'collaborator')
            result = query.run()
            projects = [m.project for m in result ]
        return projects
    
class Member(db.Model):
    user = db.UserProperty()

    @classmethod
    def get_current_member(cls):
        query = cls.all()
        query.filter('user =', users.get_current_user())
        return query.get()

    @classmethod
    def exists(cls):
        return cls.get_current_member()


class ProjectMember(db.Model):
    project = db.ReferenceProperty(Project,
#                                   required = True,
                                   collection_name='members')
    member = db.ReferenceProperty(Member,
#                                  required = True,
                                  collection_name='projects')
    status = db.StringProperty(required=True,
                               default='not collaborator',
                               choices=set(['collaborator', 'not collaborator']))

class StoryContainer(polymodel.PolyModel):
    name = db.StringProperty(required=True)
    project = db.ReferenceProperty(Project,
                                   collection_name='story_containers')
    date = db.DateTimeProperty(auto_now_add=True)
                                   
    def get_project(self):
        return self.project

    def get_containers(self):
        query = StoryContainer.all()
        query.filter('project =', self.project)
        query.order('-date')
        return query

class Iteration(StoryContainer):
    start_date = db.DateTimeProperty(required=True)
    end_date = db.DateTimeProperty(required=True)
    status = db.StringProperty(required=True,
                               default='Inactive',
                               choices=['Inactive', 'Active', 'Closed'])
    description = db.StringProperty(multiline=True)
    opened_by = db.UserProperty()
    
    def get_stories(self):
        query = Story.all()
        query.filter('story_container =', self)
        query.order('assigned_to')
        return query

    def get_active_stories(self):
        query = Story.all()
        query.filter('story_container =', self)
        query.filter('status !=', 'Closed')
        return query.run()

    def get_closed_stories(self):
        query = Story.all()
        query.filter('story_container =', self)
        query.filter('status =', 'Closed')
        return query.run()
    def count_closed_stories(self):
        query = Story.all()
        query.filter('story_container =', self)
        query.filter('status =', 'Closed')
        return query.count()
    def count_active_stories(self):
        query = Story.all()
        query.filter('story_container =', self)
        query.filter('status !=', 'Closed')
        return query.count()
    def count_stories(self):
        query = Story.all()
        query.filter('story_container =', self)
        return query.count()

    def get_user_hours(self):
        members = self.get_project().get_members()
        users_hours = []
        for member in members:
            query = Story.all()
            query.filter('story_container =', self)
            query.filter('assigned_to =', member.user)
            stories = query.run()
            hours = sum([ s.estimated_time for s in stories])
            users_hours.append([member.user.nickname(), hours])

        return users_hours
    
    def get_user_hours_worked(self):
        members = self.get_project().get_members()
        users_hours = []
        for member in members:
            query = Story.all()
            query.filter('story_container =', self)
            query.filter('assigned_to =', member.user)
            stories = query.run()
            estimated_hours = 0
            current_hours = 0
            for story in stories:
                estimated_hours += story.estimated_time
                current_hours += story.elapsed_time()
            users_hours.append([member.user.nickname(), estimated_hours, current_hours])

        return users_hours

    def get_tasks_summary(self):
        members = self.get_project().get_members()
        totals = ['Total']
        closes = ['Closed']
        pendings = ['Pending']
        summary = []
        for member in members:
            query = Story.all()
            query.filter('story_container =', self)
            query.filter('assigned_to =', member.user)
            total = query.count()
            query.filter('status =', 'Closed')
            closed = query.count()
            closes.append(closed)
            pendings.append(total - closed)
            totals.append(total)
        summary = [closes, pendings, totals]
        return summary

    def get_total_tasks_assigned(self):
        members = self.get_project().get_members()
        totals = ['Total']
        for member in members:
            query = Story.all()
            query.filter('story_container =', self)
            query.filter('assigned_to =', member.user)
            total = query.count()
            totals.append(total)
        return totals
    def planning_messages(self):
        query = PlanningMessage.all()
        query.filter("iteration =", self)
        return query.run()
    def review_messages(self):
        query = ReviewMessage.all()
        query.filter("iteration =", self)
        return query.run()

class ProjectBacklog(StoryContainer):
    def get_project(self):
        return self.project

class Story(polymodel.PolyModel):
    story_container = db.ReferenceProperty(StoryContainer,
                                           collection_name='stories')
    name = db.StringProperty(required=True)
    category = db.StringProperty(required=True,
                                 default='Feature',
                                 choices=['Feature', 'Bug'])
    priority = db.StringProperty(required=True,
                                 default='Normal',
                                 choices=['Very High', 'High', 'Normal',
                                 'Low', 'Very Low'])
    status = db.StringProperty(required=True,
                               default='Active',
                               choices=['Active', 'Implemented', 'Fixed',
                               'No Reproducible', 'Duplicated', 'Closed'])
    assigned_to = db.UserProperty()
    estimated_time = db.FloatProperty(required=True)
    description = db.StringProperty(multiline=True, default=' ')
    opened_by = db.UserProperty()
    opened_date = db.DateTimeProperty(auto_now_add=True)

    @classmethod
    def get_unfinished_tasks(cls, iteration):
        return cls.gql("where iteration=:1 and status != 'Closed'", iteration)

    def elapsed_time(self):
        elapsed_time = 0.0
        for time_worked in self.time_worked_set:
            elapsed_time = elapsed_time + time_worked.time

        return elapsed_time

    def remaining_time(self):
        return self.estimated_time - self.elapsed_time()
    
    def percentage(self):
        percentage = self.elapsed_time() * 100 / self.estimated_time
        if percentage > 100:
            percentage = 100

        return int(percentage)

    def get_justification(self):
        query = Justification.all()
        query.filter("story =", self)
        query.order('-date')
        return query.get()

    def review_comments(self):
        query = ReviewComment.all()
        query.filter("story =", self)
        return query.run()

    def planning_comments(self):
        query = PlanningComment.all()
        query.filter("story =", self)
        return query.run()

    def get_users(self):
        query = AssignEvent.all()
        query.filter("story =", self)
        users =  [ item.new_user for item in query.run() ]
        users.append(self.assigned_to)
        users.append(self.opened_by)
        return users
    def contains_current_user(self):
        return users.get_current_user() in self.get_users()

    def current_user_assigned(self):
        return users.get_current_user() == self.assigned_to

    def get_events(self):
        query = StoryEvent.all()
        query.filter('story =', self)
        query.order('-date')
        return query.run()

    @classmethod
    def get_accepted_stories(cls, candidate_iteration):
        return cls.gql("where candidate_iteration=:1 and status = 'Accepted'", candidate_iteration)
    
class StoryTimeWorked(db.Model):
    story = db.ReferenceProperty(Story,
                                 collection_name='time_worked_set')
    time = db.FloatProperty()
    reported_date = db.DateTimeProperty(auto_now_add=True)

class Event(polymodel.PolyModel):
    description = db.StringProperty(multiline=True)
    author = db.UserProperty()
    date = db.DateTimeProperty(auto_now_add=True)

    @classmethod
    def events(cls):
        query = cls.all()
        query.order('-date')
        return query.fetch(10)

class StoryEvent(Event):
    story = db.ReferenceProperty(Story,
                                 collection_name='events')

class TimeEvent(StoryEvent):
    time = db.FloatProperty()

    def title(self):
        return "Elapsed time " + str(self.time)
    def summary(self):
        return "Added elapsed time to '" + self.story.name + "' task"
    
    @classmethod
    def create(cls, story, time, description):
        model = cls(story=story, time=time, author = users.get_current_user(),
                    description=description)
        model.put()
        return model
    
class AssignEvent(StoryEvent):
    new_user = db.UserProperty()
    old_user = db.UserProperty()

    def title(self):
        return "Assigned from "+ self.old_user.nickname() + " to " + self.new_user.nickname()
    def summary(self):
        return "Assigned '" + self.story.name + "' task from "+ self.old_user.nickname() + " to " + self.new_user.nickname()
    @classmethod
    def create(cls, story, new_user, old_user, description):
        model = cls(story=story, author = users.get_current_user(),
                    description = description, new_user=new_user,
                    old_user=old_user)
        model.put()
        return model

class ResolveEvent(StoryEvent):
    new_status = db.StringProperty()
    old_status = db.StringProperty()

    def title(self):
        return "Resolved ('"+ self.old_status + "' to '"+ self.new_status +"')"

    def summary(self):
        return "Resolved '" + self.story.name + "' task "+ "('"+ self.old_status + "' to '"+ self.new_status +"')"

    @classmethod
    def create(cls, story, new_status, old_status, description):
        model = cls(story=story, author=users.get_current_user(),
                    new_status=new_status, old_status=old_status,
                    description=description)
        model.put()
        return model

class MoveEvent(StoryEvent):
    new_container = db.ReferenceProperty(StoryContainer,
                                         collection_name='new_stories')
    old_container = db.ReferenceProperty(StoryContainer,
                                         collection_name='old_stories')

    def title(self):
        return "Moved  from '"+ self.old_container.name + "' to '"+ self.new_container.name +"'"

    def summary(self):
        return "Moved '" + self.story.name + "' task "+ " from '"+ self.old_container.name + "' to '"+ self.new_container.name +"'"

    @classmethod
    def create(cls, story, old_container, new_container, description):
        model = cls(story=story, author=users.get_current_user(),
                    old_container=old_container, new_container=new_container,
                    description=description)
        model.put()
        return model

class EditDescEvent(StoryEvent):
    old_description = db.StringProperty(multiline=True)
    new_description = db.StringProperty(multiline=True)

    def title(self):
        return "Edited description"

    def summary(self):
        return "Edited '" + self.story.name + "' task "+ "description"

    @classmethod
    def create(cls, story, old_description, new_description):
        model = cls(story=story, author=users.get_current_user(),
                    old_description=old_description,
                    new_description=new_description)
        model.description = "Original:\n" + old_description + "\nLatest:\n" + new_description
        model.put()
        return model

class CommentEvent(StoryEvent):
    def title(self):
        return "Commented"

    def summary(self):
        return "Commented '" + self.story.name + "' task "

    @classmethod
    def create(cls, story, comment):
        model = cls(story=story, author=users.get_current_user(),
                    description=comment)
        model.put()
        return model

class Justification(db.Model):
    story = db.ReferenceProperty(Story,
                                 collection_name='justifications')
    author = db.UserProperty()
    text = db.StringProperty(multiline=True)
    date = db.DateTimeProperty(auto_now_add=True)

class Comment(polymodel.PolyModel):
    story = db.ReferenceProperty(Story,
                                 collection_name='comments')
    author = db.UserProperty()
    text = db.StringProperty(multiline=True)
    date = db.DateTimeProperty(auto_now_add=True)

class PlanningComment(Comment):
    pass
class ReviewComment(Comment):
    pass

class Message(polymodel.PolyModel):
    iteration = db.ReferenceProperty(Iteration,
                                     collection_name='messages')
    user = db.UserProperty()
    text = db.StringProperty(default="")
    date = db.DateTimeProperty(auto_now_add=True)

class PlanningMessage(Message):
    pass
class ReviewMessage(Message):
    pass